package dniegp2pclient;

import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.ClientResponse.Status;
import java.util.Set;
import javax.ws.rs.core.MultivaluedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.gruposp2p.dnie.crypto.PKCS11KeyStore;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.Task;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFrame;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.swing.JButton;
import net.miginfocom.swing.MigLayout;
import org.gruposp2p.dnie.cliente.rest.RestFacade;
import org.gruposp2p.dnie.crypto.Signer;
import org.gruposp2p.dnie.model.DNIeSignedDocument;
import org.gruposp2p.dnie.model.SignedDocument;
import org.gruposp2p.dnie.ui.dialogs.ConnectToServerDialog;
import org.gruposp2p.dnie.ui.dialogs.DocumentAlreadySendedDialog;
import org.gruposp2p.dnie.ui.dialogs.DocumentSendedDialog;
import org.gruposp2p.dnie.ui.dialogs.ErrorDialog;
import org.gruposp2p.dnie.ui.dialogs.PKCS11LibLoaderDialog;
import org.gruposp2p.dnie.ui.dialogs.PasswordDialog;
import org.gruposp2p.dnie.ui.panels.SignatureDocumentPanel;
import org.gruposp2p.dnie.utils.FileUtils;
import org.gruposp2p.dnie.utils.PropertyLoader;
import org.gruposp2p.dnie.xml.jaxb.DocumentMarshaller;
import org.w3c.dom.Document;


public class DNIeGP2PClientView extends FrameView {

    private static Logger logger = LoggerFactory.getLogger(DNIeGP2PClientView.class);

    private String xmlStrSignedDocument;
    private PKCS11KeyStore pkcs11KeyStore;
    private String password;
    //The Spanish Smart card has two certificates ->
    //"CertFirmaDigital" and "CertAutenticacion"
    private String certAlias = "CertFirmaDigital";
    private JFrame viewFrame;
    private SignatureDocumentPanel signatureDocumentPanel;
    private SignedDocument signedDocument;
    private JButton loadDNIeButton;
    private JButton sendButton;
    private JButton signButton;
    private ResourceMap resourceMap;

    public DNIeGP2PClientView(SingleFrameApplication app) {
        super(app);
        initComponents();
        initMainPanel();
        // status bar initialization - message timeout, idle icon and busy animation, etc
        resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText(" ");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String)(evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? " " : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer)(evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });
    }


    private void initMainPanel() {
        MigLayout layout = new MigLayout("fill");
        mainPanel.setLayout(layout);
        viewFrame = getFrame();
        javax.swing.ActionMap actionMap =
                org.jdesktop.application.Application.getInstance().
                getContext().getActionMap(DNIeGP2PClientView.class, this);

        loadDNIeButton = new javax.swing.JButton();
        loadDNIeButton.setAction(actionMap.get("loadDNIe")); // NOI18N
        loadDNIeButton.setIcon(getResourceMap().getIcon("loadDNIeButton.icon")); // NOI18N
        loadDNIeButton.setText(getResourceMap().getString("loadDNIeButton.text")); // NOI18N
        loadDNIeButton.setName("loadDNIeButton"); // NOI18N

        signatureDocumentPanel = new SignatureDocumentPanel();

        signButton = new javax.swing.JButton();
        signButton.setAction(actionMap.get("signDocument")); // NOI18N
        signButton.setIcon(getResourceMap().getIcon("signButton.icon")); // NOI18N
        signButton.setText(getResourceMap().getString("signButton.text")); // NOI18N
        signButton.setToolTipText(getResourceMap().getString("signButton.toolTipText")); // NOI18N
        signButton.setName("signButton"); // NOI18N

        sendButton = new javax.swing.JButton();
        sendButton.setAction(actionMap.get("sendDocumentToServer")); // NOI18N
        sendButton.setIcon(getResourceMap().getIcon("sendButton.icon")); // NOI18N
        sendButton.setText(getResourceMap().getString("sendButton.text")); // NOI18N
        sendButton.setName("sendButton"); // NOI18N

        //panel.add(comp2, "cell 0 0 2 1"); // "cell column row width height"
        mainPanel.add(loadDNIeButton, "cell 0 0, top, left");

        menuBar.setVisible(false);
        //mainPanel.repaint();
        //viewFrame.pack();
    }

    public void setSignedDocument(SignedDocument signedDocument) {
        this.signatureDocumentPanel.setDNIeDocumentToSign(
                signedDocument.signatureDataWrapper.dnieSignedDocument.getDnieDocumentToSignId());
        logger.debug("signedDocument : " + signedDocument.signatureDataWrapper.dnieSignedDocument.getDnieDocumentToSignId().getContent());
        this.signedDocument = signedDocument;
        mainPanel.add(signatureDocumentPanel, "cell 0 0 3 1, grow");
        if (RuntimeData.getInstance().isDNIeLoaded) {
            mainPanel.remove(sendButton);
            mainPanel.add(signButton,"cell 2 3, right");
            signButton.setEnabled(true);
        }
        mainPanel.repaint();
    }

    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = DNIeGP2PClientApp.getApplication().getMainFrame();
            aboutBox = new DNIeGP2PClientAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        DNIeGP2PClientApp.getApplication().show(aboutBox);
    }


    @Action
    public Task signDocument() {
        if (!signatureDocumentPanel.isDataValid()) {
            return null;
        }
        signButton.setEnabled(false);
        return new SignDocumentTask(getApplication());
    }

    private class SignDocumentTask extends org.jdesktop.application.Task<Object, Void> {

        SignDocumentTask(org.jdesktop.application.Application app) {
            super(app);
            signatureDocumentPanel.showSigningState(true);
        }

        @Override protected Object doInBackground() {
            try {/*
                SelectCertDialog selectCertDialog = new SelectCertDialog (
                        getFrame(), true, pkcs11KeyStore.getKeyStoreAliases());
                selectCertDialog.setVisible(true);
                switch (selectCertDialog.state) {
                    case ACCEPT:
                        certAlias = selectCertDialog.getSelectedCertAlias();
                        break;
                    case CANCEL:
                        return null;
                    default:
                        return null;
                }
                X509Certificate certificate =
                        pkcs11KeyStore.getCertificate(certAlias);*/
                X509Certificate certificate = pkcs11KeyStore.getCertificate(certAlias);
                PrivateKey privateKey = pkcs11KeyStore.getPrivateKey(
                        certAlias, password);
                DNIeSignedDocument dnieSignedDocument =
                        signatureDocumentPanel.getDNIeSignedDocument();
                Document signatureData =DocumentMarshaller.marshallObjectToDocument(
                        dnieSignedDocument);
                String xmlSignedDocument = Signer.createEnvelopingSignature(
                        signatureData,certificate, privateKey);
                logger.debug("strSignedDocument: " + xmlSignedDocument);
                return xmlSignedDocument;
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
                ErrorDialog errorDialog = new ErrorDialog(getFrame(), true);
                errorDialog.setErrorMessage(ex.getMessage());
            }
            return null;
        }
        @Override protected void succeeded(Object result) {
            if (result != null) {
                xmlStrSignedDocument = (String) result;
                mainPanel.remove(signButton);
                mainPanel.add(sendButton,"cell 2 3, right");
                sendButton.setEnabled(true);
                signatureDocumentPanel.showSignedState(true, xmlStrSignedDocument, 
                        signedDocument.signatureDataWrapper.dnieSignedDocument.getDnieDocumentToSignId().getTitle());
            } else {
                signButton.setEnabled(true);
            }
        }
    }

    @Action
    public Task sendDocumentToServer() {
        return new SendDocumentToServerTask(getApplication());
    }

    private class SendDocumentToServerTask extends
            org.jdesktop.application.Task<Object, Void> {

        SendDocumentToServerTask(org.jdesktop.application.Application app) {
            super(app);
            sendButton.setEnabled(false);
        }

        @Override protected Object doInBackground() {
            ClientResponse clientResponse = null;
            try {
                clientResponse = RestFacade.postSignedDocument(xmlStrSignedDocument);
            }
            catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
                ErrorDialog errorDialog = new ErrorDialog(
                DNIeGP2PClientApp.getApplication().getView().getFrame(), true);
                errorDialog.setErrorMessage(ex.getMessage());
            }
            return clientResponse;
        }
        @Override protected void succeeded(Object result) {
            logger.debug(" - succeeded - ");
            ClientResponse clientResponse = (ClientResponse)result;
            String response = null;
            if (clientResponse.getClientResponseStatus().equals(Status.OK)) {
                logger.debug(" - succeeded - Status.OK");
                response = clientResponse.getEntity(String.class);
                sendButton.setVisible(false);
                //mainPanel.remove(sendButton);
                DocumentSendedDialog documentSendedDialog =
                        new DocumentSendedDialog(
                        DNIeGP2PClientApp.getApplication().getView().getFrame(), true,
                        response);
                documentSendedDialog.setVisible(true);
            } else if (clientResponse.getClientResponseStatus().equals(
                Status.PRECONDITION_FAILED)) {
                logger.debug(" - succeeded - Status.PRECONDITION_FAILED");
                response = clientResponse.getEntity(String.class);
                DocumentAlreadySendedDialog documentSendedDialog =
                        new DocumentAlreadySendedDialog(
                        DNIeGP2PClientApp.getApplication().getView().getFrame(), true,
                        response);
                documentSendedDialog.setVisible(true);
            } else {
                logger.debug(" - succeeded - else - ");
                MultivaluedMap<String, String> headers = clientResponse.getHeaders();
                Set<String> keyset = headers.keySet();
                for (String key : keyset) {
                    logger.debug(" key : " + key + " - value: " + headers.get(key));
                }
                ErrorDialog errorDialog = new ErrorDialog(
                            DNIeGP2PClientApp.getApplication().getView().getFrame(), true);
                errorDialog.setErrorMessage(
                        clientResponse.getClientResponseStatus().getReasonPhrase());
                sendButton.setEnabled(true);
            }
        }
    }

    @Action
    public Task loadDNIe() {
        loadDNIeButton.setEnabled(false);
        return new LoadDNIeTask(getApplication());
    }

    private class LoadDNIeTask extends
            org.jdesktop.application.Task<Object, Void> {

        LoadDNIeTask(org.jdesktop.application.Application app) {
            // Runs on the EDT.  Copy GUI state that
            // doInBackground() depends on from parameters
            // to SignDocumentTask fields, here.
            super(app);
        }

        LoadDNIeTask(org.jdesktop.application.SingleFrameApplication app) {
            super(app);
        }

        @Override protected Object doInBackground() {
            String rutaPKCS11Lib = PropertyLoader.getProperty(
                    PropertyLoader.PKCS11_DRIVER_PATH);
            if(rutaPKCS11Lib == null || "".equals(rutaPKCS11Lib)) {
                rutaPKCS11Lib = FileUtils.getPKCS11LibPath();
                if (rutaPKCS11Lib != null) {
                    PropertyLoader.setProperty(
                            PropertyLoader.PKCS11_DRIVER_PATH, rutaPKCS11Lib);
                } else {
                    PKCS11LibLoaderDialog pkcs11LibLoaderDialog=
                            new PKCS11LibLoaderDialog (
                            DNIeGP2PClientApp.getApplication().getView().getFrame(), true);
                    pkcs11LibLoaderDialog.setVisible(true);
                    return null;
                }
            }
            PasswordDialog dialogoPassword = new PasswordDialog (
                    viewFrame, true);
            dialogoPassword.setVisible(true);
            password = dialogoPassword.getPassword();
            if (dialogoPassword.getPassword() != null) {
                try {
                    statusMessageLabel.setText(resourceMap
                        .getString("loadingDNIe.text"));
                    pkcs11KeyStore = new PKCS11KeyStore(
                            rutaPKCS11Lib, dialogoPassword.getPassword());
                } catch (Exception ex) {
                    logger.error(ex.getMessage(), ex);
                    password = null;
                    ErrorDialog errorDialog = new ErrorDialog(
                            DNIeGP2PClientApp.getApplication().getView().getFrame(), true);
                    errorDialog.setErrorMessage(ex.getLocalizedMessage());
                }
            }
            return pkcs11KeyStore;
        }
        @Override protected void succeeded(Object result) {
            if (result != null) {
                RuntimeData.getInstance().isDNIeLoaded = true;
                String documentURL = System.getProperty("documentURL");
                if (documentURL != null && !"".equals(documentURL)) {
                    statusMessageLabel.setText(resourceMap
                        .getString("loadingDNIeDocumentToSign.text"));
                    ConnectToServerDialog connectToServerDialog =
                    new ConnectToServerDialog(viewFrame, true, documentURL);
                    connectToServerDialog.setVisible(true);
                }
                mainPanel.remove(loadDNIeButton);
                menuBar.setVisible(true);
            } else {
                loadDNIeButton.setEnabled(true);
            }
            statusMessageLabel.setText(getResourceMap()
                    .getString(" "));
        }
    }

    public void setStatusMessageText (String text) {
        statusMessageLabel.setText(text);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        getDocumentMenuItem = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();

        mainPanel.setName("mainPanel"); // NOI18N

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 252, Short.MAX_VALUE)
        );

        menuBar.setName("menuBar"); // NOI18N

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance().getContext().getResourceMap(DNIeGP2PClientView.class);
        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance().getContext().getActionMap(DNIeGP2PClientView.class, this);
        exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
        exitMenuItem.setIcon(resourceMap.getIcon("exitMenuItem.icon")); // NOI18N
        exitMenuItem.setText(resourceMap.getString("exitMenuItem.text")); // NOI18N
        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        getDocumentMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        getDocumentMenuItem.setIcon(resourceMap.getIcon("getDocumentMenuItem.icon")); // NOI18N
        getDocumentMenuItem.setText(resourceMap.getString("getDocumentMenuItem.text")); // NOI18N
        getDocumentMenuItem.setName("getDocumentMenuItem"); // NOI18N
        getDocumentMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                getDocumentMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(getDocumentMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setIcon(resourceMap.getIcon("aboutMenuItem.icon")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, statusPanelLayout.createSequentialGroup()
                .addGap(6, 6, 6)
                .addComponent(statusMessageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 216, Short.MAX_VALUE)
                .addGap(18, 18, 18)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(statusAnimationLabel)
                        .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(statusMessageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3))
        );

        setComponent(mainPanel);
        setMenuBar(menuBar);
        setStatusBar(statusPanel);
    }// </editor-fold>//GEN-END:initComponents

    private void getDocumentMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_getDocumentMenuItemActionPerformed
        ConnectToServerDialog connectToServerDialog =
                new ConnectToServerDialog(this.getFrame(), true, null);
        connectToServerDialog.setVisible(true);
    }//GEN-LAST:event_getDocumentMenuItemActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem getDocumentMenuItem;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    // End of variables declaration//GEN-END:variables

    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;
    private JDialog aboutBox;
    
}
